/******************************************************************************************
Copyright(C), 2019-2020, Linyar.
文件名：L_SVP_NNIE_model.c
作者：燕卫博    版本：V1.1    创建日期：2021.5.27
文件描述：
    SVP-NNIE模型加载，仅用于适配YOLO-Fastest（2-YOLO-Layers）。
    对于使用YOLO层的其它网络可能需要修改部分参数和操作。
版本信息：
    9Hv2，个人。
历史修改记录：
1. 2021-5-27:V1.1 燕卫博
从STv8拷贝更改。

2. 2021-8-11:V1.0 燕卫博
从9Hv1拷贝，接续使用。
******************************************************************************************/
//                                              ↓
//+------------------------------------------------------------------------------------------+
//|                                      SVP-NNIE操作说明
//|      “NNIE是Neural Network Inference Engine的简称，是海思媒体SoC中专门针对神经网络特别是
//|  深度卷积神经网络进行加速处理的硬件单元。”——《HiSVP开发指南(2019-7-10)》P17
//|      使用流程是：在PC端使用深度学习框架及网络训练方法得到最终的网络模型，然后通过海思提供
//|  的工具将模型转化为开发板上可以运行的*.wk文件。目前NNIE配套软件及工具链仅支持Caffe框架，
//|  使用其他框架的网络模型需要转化为Caffe框架下的模型。
//|      在得到*.wk文件后，即可通过HiSVP提供的API进行载入和使用，从流程上而言，SVP-NNIE分为以
//|  下四个部分：
//|     1.MPP系统初始化。根据预测图大小，建立VB缓冲区。
//|     2.模型载入和参数初始化。参数初始化包括硬件参数和软件参数两部分。
//|     3.获取数据、前向计算和结果输出。这部分是最复杂的，和使用的网络有关系。
//|     4.去初始化。软件正常退出时，需要按次序去初始化软硬件参数和MPP系统。
//|  注意：使用SVP_NNIE之前需确保已启动MMP系统并分配了足够的缓冲区。
//|
//|      MPP准备完成后，载入模型、初始化网络预测环境，而后即可进行前向计算。
//|      Blob是Caffe中数据流通的一个基本类，网络各层之间的数据是通过blob来传递的。所以在对网
//|  络节点进行处理的时候，处理的对象是blob。
//|      在网络模型加载完成、参数初始化完成之后，进行前向计算，前向计算的过程分为三个阶段。分
//|  别是源数据载入、调用SVP-NNIE API进行前向计算以及对输出进行后续的处理。
//|      关于程序中申请的MMZ空间。海思的系统在内存分配的时候，为MPP业务预留的、与操作系统分离
//|  的内存空间为MMZ内存，所以调用MPP API需要开辟的内存空间均要从MMZ空间中申请。
//+------------------------------------------------------------------------------------------+
//|  编写参考《HiSVP开发指南(2019-7-10)》（简称《HiSVP》）、
//|  《HiMPP V4.0媒体处理软件开发参考(2019-8-1)》（简称《参考》）。
//+------------------------------------------------------------------------------------------+
//|                                         头文件包含
//+------------------------------------------------------------------------------------------+
/*|*/  #include "L_SVP_NNIE_model.h"
//+------------------------------------------------------------------------------------------+
//|                                        私有数据类型
//+------------------------------------------------------------------------------------------+
//|    ------------------------------------------------------------
//|    每网络段的输入输出blob信息记录结构
//|    ------------------------------------------------------------
/*|*/  typedef struct l_svp_nnie_seg_data_s
/*|*/  {
/*|*/      SVP_SRC_BLOB_S astSrc[SVP_NNIE_MAX_INPUT_NUM];
/*|*/      SVP_DST_BLOB_S astDst[SVP_NNIE_MAX_OUTPUT_NUM];
/*|*/  }SVP_NNIE_SEG_DATA_S;
//|
/*|*/  typedef struct l_quick_sort_stack_s
/*|*/  {
/*|*/      HI_S32 s32Min;
/*|*/      HI_S32 s32Max;
/*|*/  }QUICK_SORT_STACK_S;
//|
/*|*/  typedef struct l_svp_nnie_para_s
/*|*/  {
//|        ------------------------------------------------------------
//|        网络模型描述结构体
//|        NOTE:该结构体信息由网络模型加载API提供。
//|        ------------------------------------------------------------
/*|*/      SVP_NNIE_MODEL_S stModel;
//|        ------------------------------------------------------------
//|        存放模型文件的内存空间
//|        NOTE:该结构体信息由网络模型加载API提供。
//|        ------------------------------------------------------------
/*|*/      SVP_MEM_INFO_S stModelBuf;
//|        ------------------------------------------------------------
//|        任务内存信息
//|        EXPLAN:将该变量放在此处是为了方便内存释放操作。
//|        ------------------------------------------------------------
/*|*/      SVP_MEM_INFO_S stTaskBuf;
//|        ------------------------------------------------------------
//|        辅助内存信息
//|        NOTE:辅助内存大小由网络模型本身提供，在调用API获取模型描述
//|             结构体后即可得到该信息。
//|        EXPLAN:将该变量放在此处是为了方便内存释放操作。
//|        ------------------------------------------------------------
/*|*/      SVP_MEM_INFO_S stTmpBuf;
//|        ------------------------------------------------------------
//|        Blobs内存信息
//|        EXPLAN:将该变量放在此处是为了方便内存释放操作。
//|        ------------------------------------------------------------
/*|*/      SVP_MEM_INFO_S stBlobBuf;
//|        ------------------------------------------------------------
//|        每段的输入/输出blobs信息
//|        EXPLAN:此成员作为网络输入/输出blobs相关信息的中继者，从模型
//|                描述结构体获取各节点信息，并被参数初始化和前向计算过
//|                程调用。
//|        ------------------------------------------------------------
/*|*/      SVP_NNIE_SEG_DATA_S astSegData[SVP_NNIE_MAX_NET_SEG_NUM];
//|        ------------------------------------------------------------
//|        网络前向计算控制结构体
//|        NOTE:此成员包括网络前向计算所需的辅助内存和任务内存信息，
//|              用于网络前向计算API。
//|        ------------------------------------------------------------
/*|*/      SVP_NNIE_FORWARD_CTRL_S astForwardCtrl[SVP_NNIE_MAX_NET_SEG_NUM];
/*|*/  }L_NNIE_PARA_S;
//|
/*|*/  typedef struct l_nnie_yolo_para_s
/*|*/  {
//|        ------------------------------------------------------------
//|        YOLO网络层使用的Anchors
//|        NOTE:此成员具体数值与网络训练时设置的超参数有关，需针对不同
//|              网络在初始化函数中手动设置。
//|        ------------------------------------------------------------
/*|*/      HI_FLOAT af32Anchors[2][6];
//|        ------------------------------------------------------------
//|        YOLO网络层输出特征图大小
//|        NOTE:此成员具体数值与网络结构设计有关，需针对不同网络在初始
//|              化函数中手动设置。
//|        EXPLAN:Width:[][0]; Height:[][1]。
//|        ------------------------------------------------------------
/*|*/      HI_U32 af32Feature[2][2];
//|        ------------------------------------------------------------
//|        网络可检测目标分类数量
//|        NOTE:此成员具体数值与网络结构设计有关，其决定了YOLO网络层输
//|             出张量数量，需针对不同网络在初始化函数中手动设置。
//|        ------------------------------------------------------------
/*|*/      HI_U32 u32ClassNum;
//|        ------------------------------------------------------------
//|        每网格单元预测出的Bbox数量
//|        NOTE:此成员具体数值与网络训练时设置的超参数有关，需针对不同
//|              网络在初始化函数中手动设置。
//|        ------------------------------------------------------------
/*|*/      HI_U32 u32BboxNumEachCell;
//|        ------------------------------------------------------------
//|        有效Bbox缓冲区
//|        NOTE:此内存空间用于存放所有网络输出的大于目标置信度阈值的Bbox
//|             的内容，这些内容未经筛选和排序。
//|        ------------------------------------------------------------
/*|*/      SVP_MEM_INFO_S stValidBboxBuf;
//|        ------------------------------------------------------------
//|        记录ValidBboxBuf缓冲区中存放的Bbox数量
//|        ------------------------------------------------------------
/*|*/      HI_U32 u32ValidBboxNum;
//|        ------------------------------------------------------------
//|        快排辅助空间
//|        ------------------------------------------------------------
/*|*/      SVP_MEM_INFO_S stAssistStackBuf;
//|        ------------------------------------------------------------
//|        用于进行比较的目标置信度阈值
//|        NOTE:凡小于此阈值的目标均会被从结果空间中剔除
//|        ------------------------------------------------------------
/*|*/      HI_FLOAT f32ObjectProbTresh;
//|        ------------------------------------------------------------
//|        用于进行NMS的阈值
//|        NOTE:凡小于此阈值的目标均会被从结果空间中剔除
//|        ------------------------------------------------------------
/*|*/      HI_FLOAT f32NmsThresh;
//|        ------------------------------------------------------------
//|        最终输出的预测结果Bboxes数量
//|        NOTE:此成员参与输出Bbox内存空间申请和最终结果筛选过程。
//|        ------------------------------------------------------------
/*|*/      HI_U32 u32MaxDetectionNum;
//|        ------------------------------------------------------------
//|        **用于存放所有最终Bboxes内容的内存空间
//|        NOTE:此内存空间用于存放所有网络输出blobs的内容，这些内容未经
//|             筛选和排序。
//|        ------------------------------------------------------------
/*|*/      SVP_MEM_INFO_S stOutputBboxBuf;
//|        ------------------------------------------------------------
//|        最终输出的预测结果Bboxes数量
//|        NOTE:此成员表示在OutputBboxBuf空间内的有效Bbox数量
//|        ------------------------------------------------------------
/*|*/      HI_U32 u32OutputBboxNum;
/*|*/  }L_NNIE_YOLO_PARA_S;
//|
//|    ----------------------------------------
//|    用于存放YOLO网络层原始输出的Bbox信息结构体
//|    ----------------------------------------
/*|*/  typedef struct l_svp_nnie_yolo_bbox_s
/*|*/  {
/*|*/      HI_FLOAT f32Xmin;
/*|*/      HI_FLOAT f32Ymin;
/*|*/      HI_FLOAT f32Xmax;
/*|*/      HI_FLOAT f32Ymax;
//|        ------------------------------------------------------------
//|        目标置信度
//|        NOTE:目标置信度 = 边框置信度 * 条件类别概率
//|        ------------------------------------------------------------
/*|*/      HI_FLOAT f32ObjectProb;
//|        ------------------------------------------------------------
//|        条件类别标号
//|        ------------------------------------------------------------
/*|*/      HI_U32 u32ClassIndex;
//|        ------------------------------------------------------------
//|        Bbox有效掩码
//|        ------------------------------------------------------------
/*|*/      HI_U32 u32Valid;
/*|*/  }SVP_NNIE_YOLO_BBOX_S;
//+------------------------------------------------------------------------------------------+
//|                                          全局变量
//+------------------------------------------------------------------------------------------+
//|    ------------------------------------------------------------
//|    向网络段中输入的最大batch数
//|    ------------------------------------------------------------
/*|*/  static const HI_U32 u32MaxInputNum = 1;
/*|*/  static L_NNIE_PARA_S nnieparameter = {0};
/*|*/  static L_NNIE_YOLO_PARA_S yoloparameter = {0};
//|    ------------------------------------------------------------
//|    网络分类标签值
//|    ------------------------------------------------------------
/*|*/  static const char yoloclasslist[81][64] =
/*|*/  {
/*|*/      "background",
/*|*/      "person", "bicycle", "car", "motorbike", "aeroplane",
/*|*/      "bus", "train", "truck", "boat", "traffic light",
/*|*/      "fire hydrant", "stop sign", "parking meter", "bench", "bird",
/*|*/      "cat", "dog", "horse", "sheep", "cow",
/*|*/      "elephant", "bear", "zebra", "giraffe", "backpack",
/*|*/      "umbrella", "handbag", "tie", "suitcase", "frisbee",
/*|*/      "skis", "snowboard", "sports ball", "kite", "baseball bat",
/*|*/      "baseball glove", "skateboard", "surfboard", "tennis racket", "bottle",
/*|*/      "wine glass", "cup", "fork", "knife", "spoon",
/*|*/      "bowl", "banana", "apple", "sandwich", "orange",
/*|*/      "broccoli", "carrot", "hot dog", "pizza", "donut",
/*|*/      "cake", "chair", "sofa", "pottedplant", "bed",
/*|*/      "diningtable", "toilet", "tvmonitor", "laptop", "mouse",
/*|*/      "remote", "keyboard", "cell phone", "microwave", "oven",
/*|*/      "toaster", "sink", "refrigerator", "book", "clock",
/*|*/      "vase", "scissors", "teddy bear", "hair drier", "toothbrush"
/*|*/  };
//+------------------------------------------------------------------------------------------+

//+------------------------------------------------------------------------------------------+
//|  函数名称：SVP_NNIE_YOLO_IOU
//|  功能描述：计算两个Bbox的IOU
//|  参数说明：指向两Bbox的指针
//|  返回值说明：返回两Bbox的IOU
//|  备注：从SAMPLE库中修改而来。
//+------------------------------------------------------------------------------------------+
static HI_DOUBLE SVP_NNIE_YOLO_IOU(SVP_NNIE_YOLO_BBOX_S *pstBbox1, SVP_NNIE_YOLO_BBOX_S *pstBbox2)
{
    HI_FLOAT  f32InterWidth  = 0.0;
    HI_FLOAT  f32InterHeight = 0.0;
    HI_DOUBLE f64InterArea   = 0.0;
    HI_DOUBLE f64Box1Area    = 0.0;
    HI_DOUBLE f64Box2Area    = 0.0;
    HI_DOUBLE f64UnionArea   = 0.0;

    f32InterWidth  = SVP_NNIE_MIN(pstBbox1->f32Xmax, pstBbox2->f32Xmax) - SVP_NNIE_MAX(pstBbox1->f32Xmin,pstBbox2->f32Xmin);
    f32InterHeight = SVP_NNIE_MIN(pstBbox1->f32Ymax, pstBbox2->f32Ymax) - SVP_NNIE_MAX(pstBbox1->f32Ymin,pstBbox2->f32Ymin);

    if(f32InterWidth <= 0 || f32InterHeight <= 0) return 0;

    f64InterArea = f32InterWidth * f32InterHeight;
    f64Box1Area  = (pstBbox1->f32Xmax - pstBbox1->f32Xmin)* (pstBbox1->f32Ymax - pstBbox1->f32Ymin);
    f64Box2Area  = (pstBbox2->f32Xmax - pstBbox2->f32Xmin)* (pstBbox2->f32Ymax - pstBbox2->f32Ymin);
    f64UnionArea = f64Box1Area + f64Box2Area - f64InterArea;

    return f64InterArea / f64UnionArea;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：SVP_NNIE_YOLO_NonMaxSuppression
//|  功能描述：对BboxBuf空间做NMS
//|  参数说明：略
//|  返回值说明：无
//|  备注：从SAMPLE库中修改而来。
//+------------------------------------------------------------------------------------------+
static void SVP_NNIE_YOLO_NonMaxSuppression(SVP_NNIE_YOLO_BBOX_S* pstBbox, HI_U32 u32BboxNum, HI_FLOAT f32NmsThresh, HI_U32 u32MaxRoiNum)
{
    HI_U32 u32Num = 0;
    HI_DOUBLE f64Iou = 0.0;

    for (int i = 0; i < u32BboxNum && u32Num < u32MaxRoiNum; i++)
    {
        if(pstBbox[i].u32Valid == 0 )
        {
            u32Num++;
            for(int j = i + 1; j < u32BboxNum; j++)
            {
                if(pstBbox[j].u32Valid == 0 )
                {
                    f64Iou = SVP_NNIE_YOLO_IOU(&pstBbox[i], &pstBbox[j]);
                    if(f64Iou >= (HI_DOUBLE)f32NmsThresh)
                    {
                        pstBbox[j].u32Valid = 1;
                    }
                }
            }
        }
    }
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：SVP_NNIE_YOLO_Argswap
//|  功能描述：HI_S32数组内容交换
//|  参数说明：指向两数组的指针和交换空间的长度
//|  返回值说明：无
//|  备注：从SAMPLE库中修改而来。
//+------------------------------------------------------------------------------------------+
static __inline__ void SVP_NNIE_YOLO_Argswap(HI_S32* ps32Src1, HI_S32* ps32Src2, HI_U32 u32ArraySize)
{
    for(int s32Tmp=0, i = 0; i < u32ArraySize; i++)
    {
        s32Tmp = ps32Src1[i];
        ps32Src1[i] = ps32Src2[i];
        ps32Src2[i] = s32Tmp;
    }
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：SVP_NNIE_YOLO_NonRecursiveArgQuickSort
//|  功能描述：YOLO网络层输出结果快排
//|  参数说明：略
//|  返回值说明：无
//|  备注：从SAMPLE库中修改而来。
//+------------------------------------------------------------------------------------------+
static void SVP_NNIE_YOLO_NonRecursiveArgQuickSort(HI_S32 *ps32Array,  HI_S32 s32Low,
                                                   HI_S32 s32High,     HI_U32 u32ArraySize,
                                                   HI_U32 u32ScoreIdx, QUICK_SORT_STACK_S *pstStack)
{
    HI_S32 i = s32Low;
    HI_S32 j = s32High;
    HI_S32 s32Top = 0;
    HI_FLOAT f32KeyConfidence = ps32Array[u32ArraySize * s32Low + u32ScoreIdx];
    pstStack[s32Top].s32Min = s32Low;
    pstStack[s32Top].s32Max = s32High;
    while(s32Top > -1)
    {
        s32Low = pstStack[s32Top].s32Min;
        s32High = pstStack[s32Top].s32Max;
        i = s32Low;
        j = s32High;
        s32Top--;
        f32KeyConfidence = ps32Array[u32ArraySize * s32Low + u32ScoreIdx];
        while(i < j)
        {
            while((i < j) && (f32KeyConfidence > ps32Array[j * u32ArraySize + u32ScoreIdx]))j--;
            if(i < j){SVP_NNIE_YOLO_Argswap(&ps32Array[i * u32ArraySize], &ps32Array[j * u32ArraySize], u32ArraySize);i++;}

            while((i < j) && (f32KeyConfidence < ps32Array[i * u32ArraySize + u32ScoreIdx]))i++;
            if(i < j){SVP_NNIE_YOLO_Argswap(&ps32Array[i * u32ArraySize], &ps32Array[j * u32ArraySize], u32ArraySize);j--;}
        }
        if(s32Low < i-1)
        {
            s32Top++;
            pstStack[s32Top].s32Min = s32Low;
            pstStack[s32Top].s32Max = i-1;
        }
        if(s32High > i+1)
        {
            s32Top++;
            pstStack[s32Top].s32Min = i+1;
            pstStack[s32Top].s32Max = s32High;
        }
    }
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：L_SVP_NNIE_RequestMMZ
//|  功能描述：从MMZ内存空间中申请内存
//|  参数说明：SVP_MEM_INFO_S指针，申请大小，初始化填充值、内存区域描述字符串和用于DEBUG显示的字符串
//|  返回值说明：成功返回0
//|  备注：
//+------------------------------------------------------------------------------------------+
static int L_SVP_NNIE_RequestMMZ(SVP_MEM_INFO_S *pstmeminfo, HI_S32 memsize, HI_U8 pad, const char *pdespstr, const char *pdebugstr)
{
    HI_S32 s32Ret = HI_SUCCESS;
    HI_U64 u64PhyAddr = 0;
    HI_U8 *pu8VirAddr = NULL;

    if(pstmeminfo == NULL)
    {
        LOGE("[SVP-NNIE-ERROR]L_SVP_NNIE_RequestMMZ:pstmeminfo is NULL!\n");
        return -1;
    }

    if(memsize == 0)
    {
        LOGE("[SVP-NNIE-ERROR]L_SVP_NNIE_RequestMMZ:memsize is 0!\n");
        return -2;
    }

    pstmeminfo->u32Size = memsize;
    s32Ret = HI_MPI_SYS_MmzAlloc_Cached((HI_U64*)&u64PhyAddr,
                                        (void**)&pu8VirAddr,
                                        pdespstr, NULL,
                                        pstmeminfo->u32Size);
    if(s32Ret != HI_SUCCESS){LOGE("[SVP-NNIE-ERROR]HI_MPI_SYS_MmzAlloc_Cached[%s]:failed with %#x!\n", pdebugstr, s32Ret);return -3;}
    memset(pu8VirAddr, pad, pstmeminfo->u32Size);

    HI_MPI_SYS_MmzFlushCache(u64PhyAddr, (void*)pu8VirAddr, pstmeminfo->u32Size);
    pstmeminfo->u64PhyAddr = u64PhyAddr;
    pstmeminfo->u64VirAddr = (HI_U64)(HI_UL)pu8VirAddr;
    LOGD("[SVP-NNIE-DEBUG]Malloc[%s] size:%d\n", pdebugstr, pstmeminfo->u32Size);

    return 0;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：L_SVP_NNIE_ReleaseMMZ
//|  功能描述：释放从MMZ内存空间中申请的内存
//|  参数说明：SVP_MEM_INFO_S指针和用于DEBUG显示的字符串
//|  返回值说明：无
//|  备注：
//+------------------------------------------------------------------------------------------+
static void L_SVP_NNIE_ReleaseMMZ(SVP_MEM_INFO_S *pstmeminfo, const char *pdebugstr)
{
    if(0 == pstmeminfo->u64PhyAddr || 0 == pstmeminfo->u64VirAddr)
    {
        LOGD("[SVP-NNIE-DEBUG]Free:[%s] size is 0\n", pdebugstr);
        return;
    }

    HI_MPI_SYS_MmzFree(pstmeminfo->u64PhyAddr, (HI_VOID*)(HI_UL)pstmeminfo->u64VirAddr);
    pstmeminfo->u64PhyAddr = 0;
    pstmeminfo->u64VirAddr = 0;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：L_SVP_NNIE_AnalyzeYOLO
//|  功能描述：从YOLO网络层解析网络预测结果
//|  参数说明：L_NNIE_PARA_S结构体指针和L_NNIE_YOLO_PARA_S结构体指针
//|  返回值说明：无
//|  备注：将从第0网络段的第0输出节点获取数据。
//+------------------------------------------------------------------------------------------+
static void L_SVP_NNIE_AnalyzeYOLO(const L_NNIE_PARA_S *pnnieparameter, L_NNIE_YOLO_PARA_S *pyoloparameter)
{
    HI_S32   *ps32YOLOBlob        = NULL;
    SVP_NNIE_YOLO_BBOX_S *pstBbox = NULL;
    HI_U32    u32Stride           = 0;
    HI_U32    u32ChnOffset        = 0;
    HI_U32    u32BboxOffset       = 0;
    HI_U32    u32HeightOffset     = 0;

    pstBbox = (SVP_NNIE_YOLO_BBOX_S *)(HI_S32*)(HI_UL)(pyoloparameter->stValidBboxBuf.u64VirAddr);
    pyoloparameter->u32ValidBboxNum = 0;

    //------------------------------------------------------------
    // 将各YOLO网络层的输出存放到AllBboxBuf缓冲区空间
    //------------------------------------------------------------
    /* 遍历输出节点 */
    for(int i = 0; i < pnnieparameter->stModel.astSeg[0].u16DstNum; i++)
    {
        ps32YOLOBlob    = (HI_S32*)(HI_UL)(pnnieparameter->astSegData[0].astDst[i].u64VirAddr);
        u32Stride       = pnnieparameter->astSegData[0].astDst[i].u32Stride;
        u32BboxOffset   = (4 + 1 + pyoloparameter->u32ClassNum) * pyoloparameter->af32Feature[i][1] * u32Stride / sizeof(HI_S32);
        u32ChnOffset    = pyoloparameter->af32Feature[i][1] * u32Stride / sizeof(HI_S32);
        u32HeightOffset = u32Stride / sizeof(HI_S32);

        /* DEBUG:输出当前预测对应的YOLO网络层 */
        LOGD("[SVP-NNIE-DEBUG]L_SVP_NNIE_AnalyzeYOLO:YOLOLayer[%i]\n", i);

        /* 针对YOLOFeatureMap，遍历各Cell的输出值 */
        for(int h = 0; h < pyoloparameter->af32Feature[i][1]; h++)
        {
            for(int w = 0; w < pyoloparameter->af32Feature[i][0]; w++)
            {
                /* 遍历每Cell的预测Bbox数 */
                for(int b = 0; b < pyoloparameter->u32BboxNumEachCell; b++)
                {
                    HI_FLOAT f32MaxClassProb  = 0.0f;
                    HI_FLOAT f32ObjectProb    = 0.0f;
                    HI_FLOAT f32DeltaX        = 0.0f;
                    HI_FLOAT f32DeltaY        = 0.0f;
                    HI_FLOAT f32DeltaW        = 0.0f;
                    HI_FLOAT f32DeltaH        = 0.0f;
                    HI_FLOAT f32StartX        = 0.0f;
                    HI_FLOAT f32StartY        = 0.0f;
                    HI_FLOAT f32Width         = 0.0f;
                    HI_FLOAT f32Height        = 0.0f;
                    HI_U32   u32MaxClassIndex = 0;

                    /* 获取最大类概率和对应的分类号 */
                    f32ObjectProb = SVP_NNIE_SIGMOID((HI_FLOAT)(ps32YOLOBlob[b * u32BboxOffset + 4 * u32ChnOffset + h * u32HeightOffset + w])/SVP_NNIE_QUANT_BASE);
                    for(int c = 0; c < pyoloparameter->u32ClassNum; c++)
                    {
                        HI_FLOAT tmp = (HI_FLOAT)(ps32YOLOBlob[b * u32BboxOffset + (4 + 1 + c) * u32ChnOffset + h * u32HeightOffset + w])/SVP_NNIE_QUANT_BASE;
                        //tmp = SVP_NNIE_SIGMOID(tmp);//--1
                        if(f32MaxClassProb < tmp)
                        {
                            f32MaxClassProb = tmp;
                            u32MaxClassIndex = c + 1;//第0类别列为空背景
                        }
                    }
                    f32MaxClassProb = SVP_NNIE_SIGMOID(f32MaxClassProb);//--1. 将激活放在此处可减少运算量
                    f32ObjectProb = f32ObjectProb * f32MaxClassProb;

                    /* 根据阈值筛选有效的Bbox存入缓冲区 */
                    if(f32ObjectProb > pyoloparameter->f32ObjectProbTresh)
                    {
                        pstBbox[pyoloparameter->u32ValidBboxNum].f32ObjectProb = f32ObjectProb;
                        pstBbox[pyoloparameter->u32ValidBboxNum].u32ClassIndex = u32MaxClassIndex;
                        pstBbox[pyoloparameter->u32ValidBboxNum].u32Valid      = 0;

                        /* 存储原始x,y,w,h输出 */
                        f32DeltaX = (HI_FLOAT)(ps32YOLOBlob[b * u32BboxOffset + 0 * u32ChnOffset + h * u32HeightOffset + w])/SVP_NNIE_QUANT_BASE;
                        f32DeltaY = (HI_FLOAT)(ps32YOLOBlob[b * u32BboxOffset + 1 * u32ChnOffset + h * u32HeightOffset + w])/SVP_NNIE_QUANT_BASE;
                        f32DeltaW = (HI_FLOAT)(ps32YOLOBlob[b * u32BboxOffset + 2 * u32ChnOffset + h * u32HeightOffset + w])/SVP_NNIE_QUANT_BASE;
                        f32DeltaH = (HI_FLOAT)(ps32YOLOBlob[b * u32BboxOffset + 3 * u32ChnOffset + h * u32HeightOffset + w])/SVP_NNIE_QUANT_BASE;

                        /* 转换Bbox为中心点与宽高 */
                        f32StartX = ((HI_FLOAT)(w + SVP_NNIE_SIGMOID(f32DeltaX))) / pyoloparameter->af32Feature[i][0];
                        f32StartY = ((HI_FLOAT)(h + SVP_NNIE_SIGMOID(f32DeltaY))) / pyoloparameter->af32Feature[i][1];
                        f32Width  = (HI_FLOAT)(exp(f32DeltaW) * pyoloparameter->af32Anchors[i][2 * b    ]) / pnnieparameter->astSegData[0].astSrc[0].unShape.stWhc.u32Width;
                        f32Height = (HI_FLOAT)(exp(f32DeltaH) * pyoloparameter->af32Anchors[i][2 * b + 1]) / pnnieparameter->astSegData[0].astSrc[0].unShape.stWhc.u32Height;

                        /* 转换Bbox为两点坐标形式并记录（此处的两点坐标值是基于网络输入图像的归一值） */
                        pstBbox[pyoloparameter->u32ValidBboxNum].f32Xmin = (HI_FLOAT)(f32StartX - f32Width  * 0.5f);
                        pstBbox[pyoloparameter->u32ValidBboxNum].f32Ymin = (HI_FLOAT)(f32StartY - f32Height * 0.5f);
                        pstBbox[pyoloparameter->u32ValidBboxNum].f32Xmax = (HI_FLOAT)(f32StartX + f32Width  * 0.5f);
                        pstBbox[pyoloparameter->u32ValidBboxNum].f32Ymax = (HI_FLOAT)(f32StartY + f32Height * 0.5f);

                        /* DEBUG:输出大于阈值的网络预测信息 */
                        LOGD("\tXmin:%f Ymin:%f Xmax:%f Ymax:%f Prob:%f ClassIndex:%d Class:%s\n",
                                pstBbox[pyoloparameter->u32ValidBboxNum].f32Xmin,
                                pstBbox[pyoloparameter->u32ValidBboxNum].f32Ymin,
                                pstBbox[pyoloparameter->u32ValidBboxNum].f32Xmax,
                                pstBbox[pyoloparameter->u32ValidBboxNum].f32Ymax,
                                pstBbox[pyoloparameter->u32ValidBboxNum].f32ObjectProb,
                                pstBbox[pyoloparameter->u32ValidBboxNum].u32ClassIndex,
                                &yoloclasslist[pstBbox[pyoloparameter->u32ValidBboxNum].u32ClassIndex][0]);

                        /* 记录数增加 */
                        pyoloparameter->u32ValidBboxNum++;
                    }
                }
            }
        }
    }
    LOGD("[SVP-NNIE-DEBUG]L_SVP_NNIE_AnalyzeYOLO:Total ValidBboxNum is %d\n", pyoloparameter->u32ValidBboxNum);

    //------------------------------------------------------------
    // 对结果执行排序
    // NOTE:执行排序后，ValidBboxBuf空间中的Bbox结构将按照类概率从
    //      大到小依次存放。
    // NOTE:排序操作使用的是SAMPLE库中提供的快速排序算法。
    //------------------------------------------------------------
    HI_S32 *ps32AssistStackBuf = NULL;
    ps32AssistStackBuf = (HI_S32*)(HI_UL)pyoloparameter->stAssistStackBuf.u64VirAddr;
    SVP_NNIE_YOLO_NonRecursiveArgQuickSort((HI_S32*)pstBbox,
                                           0,
                                           pyoloparameter->u32ValidBboxNum - 1,
                                           sizeof(SVP_NNIE_YOLO_BBOX_S)/sizeof(HI_U32),
                                           4,
                                           (QUICK_SORT_STACK_S*)ps32AssistStackBuf);

    /* DEBUG:打印经过排序的Bbox信息 */
    LOGD("[SVP-NNIE-DEBUG]L_SVP_NNIE_AnalyzeYOLO:Sorted Bbox(es):\n");
    for(int i = 0; i < pyoloparameter->u32ValidBboxNum; i++)
    {
        LOGD("\tXmin:%f Ymin:%f Xmax:%f Ymax:%f Prob:%f ClassIndex:%d Class:%s\n",
                                                            pstBbox[i].f32Xmin,
                                                            pstBbox[i].f32Ymin,
                                                            pstBbox[i].f32Xmax,
                                                            pstBbox[i].f32Ymax,
                                                            pstBbox[i].f32ObjectProb,
                                                            pstBbox[i].u32ClassIndex,
                                                            &yoloclasslist[pstBbox[i].u32ClassIndex][0]);
    }

    //------------------------------------------------------------
    // 对结果执行NMS算法
    //------------------------------------------------------------
    SVP_NNIE_YOLO_NonMaxSuppression(pstBbox, pyoloparameter->u32ValidBboxNum, pyoloparameter->f32NmsThresh, pyoloparameter->u32MaxDetectionNum);

    //------------------------------------------------------------
    // 将结果输出到OutputBboxBuf空间
    //------------------------------------------------------------
    SVP_NNIE_YOLO_OUTPUT_BBOX_S *pstOutputBbox = NULL;
    pstOutputBbox = (SVP_NNIE_YOLO_OUTPUT_BBOX_S *)(HI_S32*)(HI_UL)(pyoloparameter->stOutputBboxBuf.u64VirAddr);
    pyoloparameter->u32OutputBboxNum = 0;
    LOGD("[SVP-NNIE-DEBUG]L_SVP_NNIE_AnalyzeYOLO:Final Bbox(es):\n");
    for(int i = 0; i < pyoloparameter->u32ValidBboxNum; i++)
    {
        if(pstBbox[i].u32Valid == 0)
        {
            pstOutputBbox[pyoloparameter->u32OutputBboxNum].u32Xmin = SVP_NNIE_MAX((HI_S32)(pstBbox[i].f32Xmin * pnnieparameter->astSegData[0].astSrc[0].unShape.stWhc.u32Width), 0);
            pstOutputBbox[pyoloparameter->u32OutputBboxNum].u32Ymin = SVP_NNIE_MAX((HI_S32)(pstBbox[i].f32Ymin * pnnieparameter->astSegData[0].astSrc[0].unShape.stWhc.u32Height), 0);
            pstOutputBbox[pyoloparameter->u32OutputBboxNum].u32Xmax = SVP_NNIE_MIN((HI_S32)(pstBbox[i].f32Xmax * pnnieparameter->astSegData[0].astSrc[0].unShape.stWhc.u32Width), pnnieparameter->astSegData[0].astSrc[0].unShape.stWhc.u32Width);
            pstOutputBbox[pyoloparameter->u32OutputBboxNum].u32Ymax = SVP_NNIE_MIN((HI_S32)(pstBbox[i].f32Ymax * pnnieparameter->astSegData[0].astSrc[0].unShape.stWhc.u32Height), pnnieparameter->astSegData[0].astSrc[0].unShape.stWhc.u32Height);
            pstOutputBbox[pyoloparameter->u32OutputBboxNum].f32ObjectProb = pstBbox[i].f32ObjectProb;
            pstOutputBbox[pyoloparameter->u32OutputBboxNum].u32ClassIndex = pstBbox[i].u32ClassIndex;

            /* DEBUG:打印最终预测信息 */
            LOGD("\tXmin:%d Ymin:%d Xmax:%d Ymax:%d Prob:%f ClassIndex:%d Class:%s\n",
                                                            pstOutputBbox[pyoloparameter->u32OutputBboxNum].u32Xmin,
                                                            pstOutputBbox[pyoloparameter->u32OutputBboxNum].u32Ymin,
                                                            pstOutputBbox[pyoloparameter->u32OutputBboxNum].u32Xmax,
                                                            pstOutputBbox[pyoloparameter->u32OutputBboxNum].u32Ymax,
                                                            pstOutputBbox[pyoloparameter->u32OutputBboxNum].f32ObjectProb,
                                                            pstOutputBbox[pyoloparameter->u32OutputBboxNum].u32ClassIndex,
                                                           &yoloclasslist[pstOutputBbox[pyoloparameter->u32OutputBboxNum].u32ClassIndex][0]);

            pyoloparameter->u32OutputBboxNum++;
        }
    }

    /* DEBUG:打印经过NMS和坐标还原的最终Bbox信息 */
    LOGD("[SVP-NNIE-DEBUG]L_SVP_NNIE_AnalyzeYOLO:Final BboxNum:%d\n", pyoloparameter->u32OutputBboxNum);
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：L_SVP_NNIE_GetResult
//|  功能描述：获取网络的预测结果
//|  参数说明：SVP_NNIE_YOLO_OUTPUT_BBOX_S结构体指针和Bbox数量变量指针
//|  返回值说明：无
//|  备注：调用函数必须保证指针指向的空间足以存储数据
//+------------------------------------------------------------------------------------------+
void L_SVP_NNIE_GetResult(SVP_NNIE_YOLO_OUTPUT_BBOX_S **ppstoutputbbox, int *poutputbboxnum)
{
    /* 解析YOLO网络层输出 */
    L_SVP_NNIE_AnalyzeYOLO(&nnieparameter, &yoloparameter);

    /* 地址传递 */
    *ppstoutputbbox = (SVP_NNIE_YOLO_OUTPUT_BBOX_S *)(HI_S32*)(HI_UL)(yoloparameter.stOutputBboxBuf.u64VirAddr);
    *poutputbboxnum = yoloparameter.u32OutputBboxNum;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：L_SVP_NNIE_LoadDataFromStruct
//|  功能描述：向网络段填充来自结构体的输入图像数据。
//|  参数说明：帧信息结构体指针和L_NNIE_PARA_S结构体指针
//|  返回值说明：成功返回0
//|  备注：数据将被加载到第0网络段的第0输入节点。数据格式为RGB
//+------------------------------------------------------------------------------------------+
static int L_SVP_NNIE_LoadDataFromStruct(const IVE_IMAGE_S *pstIVEImageInfo, const L_NNIE_PARA_S *pnnieparameter)
{
    int s32Ret = HI_SUCCESS;
    //------------------------------------------------------------
    // 传参检查
    //------------------------------------------------------------
    if(pstIVEImageInfo == NULL){LOGE("[SVP-NNIE-ERROR]RGB Struct is NULL!\n");return -1;}

    //------------------------------------------------------------
    // 校验图片尺寸
    // NOTE:本工程仅为演示，因此为减小工作量，将输入图片帧的分辨率
    //      固定为320 * 180。
    //------------------------------------------------------------
    if(pstIVEImageInfo->enType != IVE_IMAGE_TYPE_U8C3_PLANAR)
    {
        LOGE("[SVP-NNIE-ERROR]Currently only supports the pixel format of U8C3(RGB888)!\n");
        goto FAIL;
    }

    if(pstIVEImageInfo->u32Width != 320 || pstIVEImageInfo->u32Height != 180)
    {
        LOGE("[SVP-NNIE-ERROR]Currently only supports a resolution of 320*180!\n");
        goto FAIL;
    }

    //------------------------------------------------------------
    // 格式检查， 当前仅支持HI_U8类型输入
    //------------------------------------------------------------
    if(SVP_BLOB_TYPE_U8 != pnnieparameter->astSegData[0].astSrc[0].enType)
    {
        LOGE("[SVP-NNIE-ERROR]Unprocessed source blob type! Please do code review. Seg[0]-Node[0]\n");
        goto FAIL;
    }

    //------------------------------------------------------------
    // 对待检测的图片进行padding操作
    // NOTE:padding的方法为：首先开辟一片固定的内存空间，大小与网
    //      络层输入张量相同，将该内存空间按像素格式填充灰度，而
    //      后每次加载图片帧时仅将图片帧拷贝到内存空间的固定偏移处
    //      即可。
    // ATTENTION:对输入节点内存空间填充BGR灰度的操作在节点初始化函
    //           数中进行！！
    //------------------------------------------------------------
    // Nothing to do.

    //------------------------------------------------------------
    // 将图像填充到网络输入节点的内存空间中
    // ATTENTION:除非了解数据在内存中的分布，不要盲目修改以下代码。
    //------------------------------------------------------------
    HI_U8 *pu8SrcBlobAddr = (HI_U8*)(HI_UL)(pnnieparameter->astSegData[0].astSrc[0].u64VirAddr);
    if(pu8SrcBlobAddr == NULL){LOGE("[SVP-NNIE-ERROR]Source blob pointer is NULL!\n");goto FAIL;}

    const HI_U32 u32StartOffset = 70  * pnnieparameter->astSegData[0].astSrc[0].u32Stride;
    const HI_U32 u32BlueOffset  = 320 * pnnieparameter->astSegData[0].astSrc[0].u32Stride * 0;
    const HI_U32 u32GreenOffset = 320 * pnnieparameter->astSegData[0].astSrc[0].u32Stride * 1;
    const HI_U32 u32RedOffset   = 320 * pnnieparameter->astSegData[0].astSrc[0].u32Stride * 2;

    for(int j = 0; j < pnnieparameter->astSegData[0].astSrc[0].unShape.stWhc.u32Height; j++)
    {
        /* 从IVE_Image的RGB拷贝到SrcBlob的BGR */
        memcpy(pu8SrcBlobAddr + u32BlueOffset  + u32StartOffset + j * pnnieparameter->astSegData[0].astSrc[0].u32Stride, (HI_U8*)(HI_UL)pstIVEImageInfo->au64VirAddr[2] + j * pstIVEImageInfo->au32Stride[2], pstIVEImageInfo->au32Stride[2]);
        memcpy(pu8SrcBlobAddr + u32GreenOffset + u32StartOffset + j * pnnieparameter->astSegData[0].astSrc[0].u32Stride, (HI_U8*)(HI_UL)pstIVEImageInfo->au64VirAddr[1] + j * pstIVEImageInfo->au32Stride[1], pstIVEImageInfo->au32Stride[1]);
        memcpy(pu8SrcBlobAddr + u32RedOffset   + u32StartOffset + j * pnnieparameter->astSegData[0].astSrc[0].u32Stride, (HI_U8*)(HI_UL)pstIVEImageInfo->au64VirAddr[0] + j * pstIVEImageInfo->au32Stride[0], pstIVEImageInfo->au32Stride[0]);

        /* Only for test */
        // memcpy(pu8SrcBlobAddr + u32BlueOffset  + u32StartOffset + j * pnnieparameter->astSegData[0].astSrc[0].u32Stride, (HI_U8*)(HI_UL)pstIVEImageInfo->au64VirAddr[0] + j * pstIVEImageInfo->au32Stride[0], pstIVEImageInfo->au32Stride[0]);
        // memcpy(pu8SrcBlobAddr + u32GreenOffset + u32StartOffset + j * pnnieparameter->astSegData[0].astSrc[0].u32Stride, (HI_U8*)(HI_UL)pstIVEImageInfo->au64VirAddr[1] + j * pstIVEImageInfo->au32Stride[1], pstIVEImageInfo->au32Stride[1]);
        // memcpy(pu8SrcBlobAddr + u32RedOffset   + u32StartOffset + j * pnnieparameter->astSegData[0].astSrc[0].u32Stride, (HI_U8*)(HI_UL)pstIVEImageInfo->au64VirAddr[2] + j * pstIVEImageInfo->au32Stride[2], pstIVEImageInfo->au32Stride[2]);
    }

    //------------------------------------------------------------
    //  输入blob缓存同步
    //------------------------------------------------------------
    s32Ret = HI_MPI_SYS_MmzFlushCache(pnnieparameter->astSegData[0].astSrc[0].u64PhyAddr,
                    (HI_VOID*)(HI_UL)(pnnieparameter->astSegData[0].astSrc[0].u64VirAddr),
                                      pnnieparameter->astSegData[0].astSrc[0].u32Num *
                                      pnnieparameter->astSegData[0].astSrc[0].unShape.stWhc.u32Chn *
                                      pnnieparameter->astSegData[0].astSrc[0].unShape.stWhc.u32Height *
                                      pnnieparameter->astSegData[0].astSrc[0].u32Stride);
    if(HI_SUCCESS != s32Ret){LOGE("[SVP-NNIE-ERROR]HI_MPI_SYS_MmzFlushCache:failed with %#x!\n", s32Ret);goto FAIL;}

    return 0;

FAIL:
    return -1;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：L_SVP_NNIE_LoadDataFromFile
//|  功能描述：向网络段填充来自文件的输入图像数据。
//|  参数说明：图片文件路径和L_NNIE_PARA_S结构体指针
//|  返回值说明：成功返回0
//|  备注：数据将被加载到第0网络段的第0输入节点。对来自文件的图片的预测没有处理速度要求。
//+------------------------------------------------------------------------------------------+
static int L_SVP_NNIE_LoadDataFromFile(const char *ppath_to_bgr_file, L_NNIE_PARA_S *pnnieparameter)
{
    int ret = 0, s32Ret = HI_SUCCESS;
    long filesize = 0;

    /* 传参检查 */
    if(ppath_to_bgr_file == NULL){LOGE("[SVP-NNIE-ERROR]BGR file is NULL!\n");return -1;}
    LOGI("[SVP-NNIE-INFO]Open BGR file:%s...\n", ppath_to_bgr_file);

    /* 打开图像文件 */
    FILE *fp = fopen(ppath_to_bgr_file, "rb");
    if(fp == NULL){LOGE("[SVP-NNIE-ERROR]fopen:Cannot open BGR file!\n");goto FAIL_0;}
    ret = fseek(fp, 0L, SEEK_END);
    if(ret < 0){LOGE("[SVP-NNIE-ERROR]fseek:Cannot move BGR file pointer to end!\n");goto FAIL_0;}
    filesize = ftell(fp);
    if(filesize < 0){LOGE("[SVP-NNIE-ERROR]ftell:Unable to calculate BGR file size!\n");goto FAIL_0;}
    ret = fseek(fp, 0L, SEEK_SET);
    if(ret < 0){LOGE("[SVP-NNIE-ERROR]fseek:Cannot move BGR file pointer to start!\n");goto FAIL_0;}
    LOGD("[SVP-NNIE-DEBUG]BGR file size:%ld\n", filesize);

    /* 格式检查， 当前仅支持HI_U8类型输入 */
    if(SVP_BLOB_TYPE_U8 != pnnieparameter->astSegData[0].astSrc[0].enType)
    {
        LOGE("[SVP-NNIE-ERROR]Unprocessed source blob type! Please do code review. Seg[0]-Node[0]\n");
        goto FAIL_0;
    }

    /* 数据读取和装载（数据将被加载到第0网络段的第0节点） */
    HI_U8* pu8SrcBlobAddr = (HI_U8*)(HI_UL)(pnnieparameter->astSegData[0].astSrc[0].u64VirAddr);
    if(pu8SrcBlobAddr == NULL){LOGE("[SVP-NNIE-ERROR]Source blob pointer is NULL!\n");goto FAIL_0;}

    for(int n = 0; n < pnnieparameter->astSegData[0].astSrc[0].u32Num; n++)
    {
        for(int i = 0; i < pnnieparameter->astSegData[0].astSrc[0].unShape.stWhc.u32Chn; i++)
        {
            for(int j = 0; j < pnnieparameter->astSegData[0].astSrc[0].unShape.stWhc.u32Height; j++)
            {
                ret = fread(pu8SrcBlobAddr, pnnieparameter->astSegData[0].astSrc[0].unShape.stWhc.u32Width * sizeof(HI_U8), 1, fp);
                if(ret != 1)
                {
                    LOGE("[SVP-NNIE-ERROR]fread(U8):Cannot read BGR file!\n");
                    goto FAIL_0;
                }
                pu8SrcBlobAddr += pnnieparameter->astSegData[0].astSrc[0].u32Stride;
            }
        }
    }

    /* 输入blob缓存同步 */
    s32Ret = HI_MPI_SYS_MmzFlushCache(pnnieparameter->astSegData[0].astSrc[0].u64PhyAddr,
                    (HI_VOID*)(HI_UL)(pnnieparameter->astSegData[0].astSrc[0].u64VirAddr),
                                      pnnieparameter->astSegData[0].astSrc[0].u32Num *
                                      pnnieparameter->astSegData[0].astSrc[0].unShape.stWhc.u32Chn *
                                      pnnieparameter->astSegData[0].astSrc[0].unShape.stWhc.u32Height *
                                      pnnieparameter->astSegData[0].astSrc[0].u32Stride);
    if(HI_SUCCESS != s32Ret){LOGE("[SVP-NNIE-ERROR]HI_MPI_SYS_MmzFlushCache:failed with %#x!\n", s32Ret);goto FAIL_0;}

    fclose(fp);
    return 0;

FAIL_0:
    if(fp != NULL)
    {
        fclose(fp);
    }
    return -1;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：L_SVP_NNIE_LoadData
//|  功能描述：向网络段填充输入图像数据。
//|  参数说明：BGR图片文件路径，IVE_IMAGE_S结构体地址
//|  返回值说明：成功返回0
//|  备注：使用两参数之一，路径优先。
//+------------------------------------------------------------------------------------------+
int L_SVP_NNIE_LoadData(const char *ppath_to_bgr_file, const IVE_IMAGE_S *pstIVEImageInfo)
{
    int ret = 0;
    if(ppath_to_bgr_file != NULL)
    {
        ret = L_SVP_NNIE_LoadDataFromFile(ppath_to_bgr_file, &nnieparameter);
        if(ret < 0){LOGE("[SVP-NNIE-ERROR]L_SVP_NNIE_LoadData:failed with %d!\n", ret);return -1;}
    }
    else
    {
        if(pstIVEImageInfo != NULL)
        {
            ret = L_SVP_NNIE_LoadDataFromStruct(pstIVEImageInfo, &nnieparameter);
            if(ret < 0){LOGE("[SVP-NNIE-ERROR]L_SVP_NNIE_LoadData:failed with %d!\n", ret);return -1;}
        }
        else
        {
            LOGE("[SVP-NNIE-ERROR]Data is NULL!\n");
            return -2;
        }
    }

    return 0;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：L_SVP_NNIE_ForwardCalculate
//|  功能描述：执行网络模型的前项预测过程。
//|  参数说明：L_NNIE_PARA_S结构体指针
//|  返回值说明：成功返回0
//|  备注：将执行第0网络段的前向预测。
//+------------------------------------------------------------------------------------------+
static int L_SVP_NNIE_ForwardCalculate(const L_NNIE_PARA_S *pnnieparameter)
{
    HI_S32 s32Ret = HI_SUCCESS;
    SVP_NNIE_HANDLE hNNIEHandle = 0;
    HI_BOOL bFinish = HI_FALSE;
    const HI_BOOL bInstant = HI_TRUE;

    /* 前向计算控制结构体缓存同步 */
    s32Ret = HI_MPI_SYS_MmzFlushCache(pnnieparameter->astForwardCtrl[0].stTskBuf.u64PhyAddr,
                    (HI_VOID*)(HI_UL)(pnnieparameter->astForwardCtrl[0].stTskBuf.u64VirAddr),
                                      pnnieparameter->astForwardCtrl[0].stTskBuf.u32Size);
    if(HI_SUCCESS != s32Ret){LOGE("[SVP-NNIE-ERROR]HI_MPI_SYS_MmzFlushCache:failed with %#x!\n", s32Ret);return -3;}

    /* 前向计算（将执行第0网络段的前向预测） */
    s32Ret = HI_MPI_SVP_NNIE_Forward(&hNNIEHandle,
                                      pnnieparameter->astSegData[0].astSrc,
                                     &pnnieparameter->stModel,
                                      pnnieparameter->astSegData[0].astDst,
                                     &pnnieparameter->astForwardCtrl[0],
                                      bInstant);
    if(HI_SUCCESS != s32Ret){LOGE("[SVP-NNIE-ERROR]HI_MPI_SVP_NNIE_Forward:failed with %#x!\n",s32Ret);return -1;}

    /* 等待计算完成 */
    s32Ret = HI_MPI_SVP_NNIE_Query(pnnieparameter->astForwardCtrl[0].enNnieId, hNNIEHandle, &bFinish, HI_TRUE);
    if(HI_SUCCESS != s32Ret){LOGE("[SVP-NNIE-ERROR]HI_MPI_SVP_NNIE_Query:failed with %#x!\n", s32Ret);return -2;}

    /* 输出blobs缓存同步 */
    for(int i = 0; i < pnnieparameter->astForwardCtrl[0].u32DstNum; i++)
    {
        s32Ret = HI_MPI_SYS_MmzFlushCache(pnnieparameter->astSegData[0].astDst[i].u64PhyAddr,
                        (HI_VOID*)(HI_UL)(pnnieparameter->astSegData[0].astDst[i].u64VirAddr),
                                          pnnieparameter->astSegData[0].astDst[i].u32Num *
                                          pnnieparameter->astSegData[0].astDst[i].unShape.stWhc.u32Chn *
                                          pnnieparameter->astSegData[0].astDst[i].unShape.stWhc.u32Height *
                                          pnnieparameter->astSegData[0].astDst[i].u32Stride);
        if(HI_SUCCESS != s32Ret){LOGE("[SVP-NNIE-ERROR]HI_MPI_SYS_MmzFlushCache:failed with %#x!\n", s32Ret);return -3;}
    }

    LOGD("[SVP-NNIE-DEBUG]NNIE forward calculation done.\n");
    return 0;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：L_SVP_NNIE_Forward
//|  功能描述：执行网络模型的前项预测过程。
//|  参数说明：无
//|  返回值说明：成功返回0
//|  备注：
//+------------------------------------------------------------------------------------------+
int L_SVP_NNIE_Forward(void)
{
    return L_SVP_NNIE_ForwardCalculate(&nnieparameter);
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：L_SVP_NNIE_DePrepareOutput
//|  功能描述：释放为网络输出处理过程申请的MMZ内存空间。
//|  参数说明：L_NNIE_YOLO_PARA_S结构体指针
//|  返回值说明：无
//|  备注：
//+------------------------------------------------------------------------------------------+
static void L_SVP_NNIE_DePrepareOutput(L_NNIE_YOLO_PARA_S *pyoloparameter)
{
    /* 释放ValidBboxBuf内存空间 */
    L_SVP_NNIE_ReleaseMMZ(&pyoloparameter->stValidBboxBuf, "ValidBboxBuf");

    /* 释放AssistStackBuf内存空间 */
    L_SVP_NNIE_ReleaseMMZ(&pyoloparameter->stAssistStackBuf, "AssistStackBuf");

    /* 释放OutputBboxBuf内存空间 */
    L_SVP_NNIE_ReleaseMMZ(&pyoloparameter->stOutputBboxBuf, "OutputBboxBuf");
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：L_SVP_NNIE_PrepareOutput
//|  功能描述：为处理网络输出申请内存空间并初始化网络处理过程中需要的参数。
//|  参数说明：L_NNIE_PARA_S结构体指针和L_NNIE_YOLO_PARA_S结构体指针
//|  返回值说明：成功返回0
//|  备注：仅用于处理最终输出为YOLO网络层的单网络段模型。
//+------------------------------------------------------------------------------------------+
static int L_SVP_NNIE_PrepareOutput(const L_NNIE_PARA_S *pnnieparameter, L_NNIE_YOLO_PARA_S *pyoloparameter)
{
    //------------------------------------------------------------
    // 初始化网络结果处理需要的参数
    // NOTE:这里初始化后续操作需要的静态参数，如Anchors、分类数、
    //      阈值等。
    //------------------------------------------------------------
    pyoloparameter->af32Feature[0][0]  = 10;
    pyoloparameter->af32Feature[0][1]  = 10;
    pyoloparameter->af32Feature[1][0]  = 20;
    pyoloparameter->af32Feature[1][1]  = 20;
    pyoloparameter->af32Anchors[0][0]  = 115;
    pyoloparameter->af32Anchors[0][1]  = 73;
    pyoloparameter->af32Anchors[0][2]  = 119;
    pyoloparameter->af32Anchors[0][3]  = 199;
    pyoloparameter->af32Anchors[0][4]  = 242;
    pyoloparameter->af32Anchors[0][5]  = 238;
    pyoloparameter->af32Anchors[1][0]  = 12;
    pyoloparameter->af32Anchors[1][1]  = 18;
    pyoloparameter->af32Anchors[1][2]  = 37;
    pyoloparameter->af32Anchors[1][3]  = 49;
    pyoloparameter->af32Anchors[1][4]  = 52;
    pyoloparameter->af32Anchors[1][5]  = 132;
    pyoloparameter->u32ClassNum        = 80;
    pyoloparameter->u32BboxNumEachCell = 3;
    pyoloparameter->u32MaxDetectionNum = MAX_DETECTION_NUM;
    pyoloparameter->f32ObjectProbTresh = 0.5f;
    pyoloparameter->f32NmsThresh       = 0.5f;

    //------------------------------------------------------------
    // 计算总Bbox数量
    // ATTENTION:仅根据相关参数统计第0网络段输出blobs。
    //------------------------------------------------------------
    HI_U32 u32TotalBboxNum = 0;
    for(int i = 0; i < pnnieparameter->stModel.astSeg[0].u16DstNum; i++)
    {
        u32TotalBboxNum += pyoloparameter->af32Feature[i][0] * pyoloparameter->af32Feature[i][1] * pyoloparameter->u32BboxNumEachCell;
    }

    //------------------------------------------------------------
    // 申请用于存放所有原始预测结果Bboxes的MMZ空间
    //------------------------------------------------------------
    HI_U32 u32TotalBboxSize = 0;
    /* 计算所有Bbox大小之和 */
    u32TotalBboxSize = u32TotalBboxNum * sizeof(SVP_NNIE_YOLO_BBOX_S);
    /* 为所有原始Bboxes开辟内存空间 */
    if(L_SVP_NNIE_RequestMMZ(&pyoloparameter->stValidBboxBuf, u32TotalBboxSize, 0, "l_yolo_ValidBboxBuf", "ValidBboxBuf") < 0)
    {
        goto FAIL;
    }

    //------------------------------------------------------------
    // 申请用于快速排序的辅助空间
    //------------------------------------------------------------
    HI_U32 u32AssistStackSize = 0;
    u32AssistStackSize = u32TotalBboxNum * sizeof(QUICK_SORT_STACK_S);
    if(L_SVP_NNIE_RequestMMZ(&pyoloparameter->stAssistStackBuf, u32AssistStackSize, 0, "l_yolo_AssistStackBuf", "AssistStackBuf") < 0)
    {
        goto FAIL;
    }

    //------------------------------------------------------------
    // 申请用于存放最终结果输出的MMZ空间
    //------------------------------------------------------------
    if(L_SVP_NNIE_RequestMMZ(&pyoloparameter->stOutputBboxBuf, pyoloparameter->u32MaxDetectionNum * sizeof(SVP_NNIE_YOLO_OUTPUT_BBOX_S), 0, "l_yolo_OutputBboxBuf", "OutputBboxBuf") < 0)
    {
        goto FAIL;
    }

    return 0;
FAIL:
    L_SVP_NNIE_DePrepareOutput(&yoloparameter);
    return -1;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：L_SVP_NNIE_DePrepareModel
//|  功能描述：释放在初始化过程中申请的MMZ内存。
//|  参数说明：L_NNIE_PARA_S结构体指针
//|  返回值说明：无
//|  备注：
//+------------------------------------------------------------------------------------------+
static void L_SVP_NNIE_DePrepareModel(L_NNIE_PARA_S *pnnieparameter)
{
    /* 释放TaskBuf内存空间 */
    L_SVP_NNIE_ReleaseMMZ(&pnnieparameter->stTaskBuf, "TaskBuf");

    /* 释放TmpBuf内存空间 */
    L_SVP_NNIE_ReleaseMMZ(&pnnieparameter->stTmpBuf, "TmpBuf");

    /* 释放BlobBuf内存空间 */
    L_SVP_NNIE_ReleaseMMZ(&pnnieparameter->stBlobBuf, "BlobBuf");
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：L_SVP_NNIE_PrepareModel
//|  功能描述：分析模型，为每段网络开辟MMZ内存，获取各分段信息并构建前向计算控制结构体。
//|  参数说明：L_NNIE_PARA_S结构体指针
//|  返回值说明：成功返回0
//|  备注：
//+------------------------------------------------------------------------------------------+
static int L_SVP_NNIE_PrepareModel(L_NNIE_PARA_S *pnnieparameter)
{
    HI_S32 s32Ret = HI_SUCCESS;

    //------------------------------------------------------------
    // 网络分段输入/输出blobs信息提取和类型检查
    // NOTE:此处主要从模型描述结构体获取输入/输出blobs相关信息，
    //      用于后续的数据输入和结果获取。
    // ATTENTION:网络blob节点类型与网络传递的数据有关，当前仅支持
    //           目标模型具有的节点类型。
    //------------------------------------------------------------
    for(int k = 0; k < pnnieparameter->stModel.u32NetSegNum; k++)
    {
        /* 获取各网络段源blobs信息 */
        for(int i = 0; i < pnnieparameter->stModel.astSeg[k].u16SrcNum; i++)
        {
            /* 类型检查，当前不支持向量和YUV类型 */
            if(SVP_BLOB_TYPE_S32 != pnnieparameter->stModel.astSeg[k].astSrcNode[i].enType &&
               SVP_BLOB_TYPE_U8 != pnnieparameter->stModel.astSeg[k].astSrcNode[i].enType)
            {
                LOGE("[SVP-NNIE-ERROR]Unprocessed source blob(s)! Please do code review. Seg[%d]-Node[%d]\n", k, i);
                return -1;
            }
            pnnieparameter->astSegData[k].astSrc[i].enType                  = pnnieparameter->stModel.astSeg[k].astSrcNode[i].enType;
            pnnieparameter->astSegData[k].astSrc[i].unShape.stWhc.u32Chn    = pnnieparameter->stModel.astSeg[k].astSrcNode[i].unShape.stWhc.u32Chn;
            pnnieparameter->astSegData[k].astSrc[i].unShape.stWhc.u32Height = pnnieparameter->stModel.astSeg[k].astSrcNode[i].unShape.stWhc.u32Height;
            pnnieparameter->astSegData[k].astSrc[i].unShape.stWhc.u32Width  = pnnieparameter->stModel.astSeg[k].astSrcNode[i].unShape.stWhc.u32Width;
            pnnieparameter->astSegData[k].astSrc[i].u32Num                  = u32MaxInputNum;
        }

        /* 获取各网络段输出blobs信息 */
        for(int i = 0; i < pnnieparameter->stModel.astSeg[k].u16DstNum; i++)
        {
            /* 类型检查，当前不支持向量和YUV类型 */
            if(SVP_BLOB_TYPE_S32 != pnnieparameter->stModel.astSeg[k].astSrcNode[i].enType &&
               SVP_BLOB_TYPE_U8 != pnnieparameter->stModel.astSeg[k].astSrcNode[i].enType)
            {
                LOGE("[SVP-NNIE-ERROR]Unprocessed destination blob(s)! Please do code review. Seg[%d]-Node[%d]\n", k, i);
                return -1;
            }
            pnnieparameter->astSegData[k].astDst[i].enType                  = pnnieparameter->stModel.astSeg[k].astDstNode[i].enType;
            pnnieparameter->astSegData[k].astDst[i].unShape.stWhc.u32Chn    = pnnieparameter->stModel.astSeg[k].astDstNode[i].unShape.stWhc.u32Chn;
            pnnieparameter->astSegData[k].astDst[i].unShape.stWhc.u32Height = pnnieparameter->stModel.astSeg[k].astDstNode[i].unShape.stWhc.u32Height;
            pnnieparameter->astSegData[k].astDst[i].unShape.stWhc.u32Width  = pnnieparameter->stModel.astSeg[k].astDstNode[i].unShape.stWhc.u32Width;
            pnnieparameter->astSegData[k].astDst[i].u32Num                  = u32MaxInputNum;
        }
    }

    //------------------------------------------------------------
    // 获取各网络段需要的任务内存大小，并为其申请内存。
    // NOTE:当前设置中，固定网络层中不含有RPN层，因此API第二参数为0。
    //------------------------------------------------------------
    HI_U32 au32TaskBufSize[SVP_NNIE_MAX_NET_SEG_NUM];
    HI_U32 u32TotalTaskBufSize = 0;
    s32Ret = HI_MPI_SVP_NNIE_GetTskBufSize(u32MaxInputNum, 0, &pnnieparameter->stModel,
                                           au32TaskBufSize,
                                           pnnieparameter->stModel.u32NetSegNum);
    if(s32Ret != HI_SUCCESS){LOGE("[SVP-NNIE-ERROR]HI_MPI_SVP_NNIE_GetTskBufSize:failed with %#x!\n", s32Ret);return -1;}

    for(int i = 0; i < pnnieparameter->stModel.u32NetSegNum; i++)
    {
        u32TotalTaskBufSize += au32TaskBufSize[i];
    }
    if(L_SVP_NNIE_RequestMMZ(&pnnieparameter->stTaskBuf, u32TotalTaskBufSize, 0, "l_svp_nnie_TaskBuf", "TaskBuf") < 0)
    {
        goto FAIL;
    }

    //------------------------------------------------------------
    // 计算网络模型本身需要的辅助内存大小，并为其申请内存。
    //------------------------------------------------------------
    if(L_SVP_NNIE_RequestMMZ(&pnnieparameter->stTmpBuf, pnnieparameter->stModel.u32TmpBufSize, 0, "l_svp_nnie_TmpBuf", "TmpBuf") < 0)
    {
        goto FAIL;
    }

    //------------------------------------------------------------
    // 计算各网络段输入/输出数据结构（blob）大小，并为其申请内存。
    // NOTE:当前假定整个网络仅有一个网络段接收外部输入，因此源blob
    //      内存空间仅需申请一个；但各网络段的输出blobs仍需分别分配。
    //------------------------------------------------------------
    HI_U64 u64PhyAddr = 0;
    HI_U8 *pu8VirAddr = NULL;
    HI_U32 u32Stride = 0;
    HI_U32 u32Size = 0;
    HI_U32 u32TotalBlobBufSize = 0;
    /* 临时记录各网络段需要的blobs大小，用于后续的空间分配 */
    HI_U32 au32SrcSize[SVP_NNIE_MAX_NET_SEG_NUM][SVP_NNIE_MAX_INPUT_NUM]  = {0};
    HI_U32 au32DstSize[SVP_NNIE_MAX_NET_SEG_NUM][SVP_NNIE_MAX_OUTPUT_NUM] = {0};
    /* 计算第0网络段源blob所需内存大小 */
    for(int i = 0; i < pnnieparameter->stModel.astSeg[0].u16SrcNum; i++)
    {
        if(SVP_BLOB_TYPE_S32 == pnnieparameter->stModel.astSeg[0].astSrcNode[i].enType)
        {
            u32Size = sizeof(HI_U32);
        }
        else
        {
            u32Size = sizeof(HI_U8);
        }
        u32Stride = SVP_NNIE_ALIGN256(pnnieparameter->stModel.astSeg[0].astSrcNode[i].unShape.stWhc.u32Width * u32Size);
        //LOGD("[SVP-NNIE-DEBUG]Seg[0]-SrcNode[%d].Width = %d\n", i, pnnieparameter->stModel.astSeg[0].astSrcNode[i].unShape.stWhc.u32Width);
        au32SrcSize[0][i] = pnnieparameter->astSegData[0].astSrc[i].u32Num * \
                            u32Stride * \
                            pnnieparameter->astSegData[0].astSrc[i].unShape.stWhc.u32Height * \
                            pnnieparameter->astSegData[0].astSrc[i].unShape.stWhc.u32Chn;
        pnnieparameter->astSegData[0].astSrc[i].u32Stride = u32Stride;
        LOGD("[SVP-NNIE-DEBUG]Seg[0]-SrcNode[%d].u32Width  = %d\n", i, pnnieparameter->astSegData[0].astSrc[i].unShape.stWhc.u32Width);
        LOGD("[SVP-NNIE-DEBUG]Seg[0]-SrcNode[%d].u32Height = %d\n", i, pnnieparameter->astSegData[0].astSrc[i].unShape.stWhc.u32Height);
        LOGD("[SVP-NNIE-DEBUG]Seg[0]-SrcNode[%d].u32Chn    = %d\n", i, pnnieparameter->astSegData[0].astSrc[i].unShape.stWhc.u32Chn);
        LOGD("[SVP-NNIE-DEBUG]Seg[0]-SrcNode[%d].u32Stride = %d\n", i, pnnieparameter->astSegData[0].astSrc[i].u32Stride);
        u32TotalBlobBufSize += au32SrcSize[0][i];
    }
    /* 计算各网络段输出blobs所需内存大小 */
    for(int k = 0; k < pnnieparameter->stModel.u32NetSegNum; k++)
    {
        for(int i = 0; i < pnnieparameter->stModel.astSeg[k].u16DstNum; i++)
        {
            if(SVP_BLOB_TYPE_S32 == pnnieparameter->stModel.astSeg[k].astDstNode[i].enType)
            {
                u32Size = sizeof(HI_U32);
            }
            else
            {
                u32Size = sizeof(HI_U8);
            }
            u32Stride = SVP_NNIE_ALIGN256(pnnieparameter->stModel.astSeg[k].astDstNode[i].unShape.stWhc.u32Width * u32Size);
            au32DstSize[k][i] = pnnieparameter->astSegData[k].astDst[i].u32Num * \
                                u32Stride * \
                                pnnieparameter->astSegData[k].astDst[i].unShape.stWhc.u32Height * \
                                pnnieparameter->astSegData[k].astDst[i].unShape.stWhc.u32Chn;
            pnnieparameter->astSegData[k].astDst[i].u32Stride = u32Stride;
            LOGD("[SVP-NNIE-DEBUG]Seg[%d]-DstNode[%d].u32Width  = %d\n", k, i, pnnieparameter->astSegData[k].astDst[i].unShape.stWhc.u32Width);
            LOGD("[SVP-NNIE-DEBUG]Seg[%d]-DstNode[%d].u32Height = %d\n", k, i, pnnieparameter->astSegData[k].astDst[i].unShape.stWhc.u32Height);
            LOGD("[SVP-NNIE-DEBUG]Seg[%d]-DstNode[%d].u32Chn    = %d\n", k, i, pnnieparameter->astSegData[k].astDst[i].unShape.stWhc.u32Chn);
            LOGD("[SVP-NNIE-DEBUG]Seg[%d]-DstNode[%d].u32Stride = %d\n", k, i, pnnieparameter->astSegData[k].astDst[i].u32Stride);
            u32TotalBlobBufSize += au32DstSize[k][i];
        }
    }
    /* 申请blobs内存 */
    if(L_SVP_NNIE_RequestMMZ(&pnnieparameter->stBlobBuf, u32TotalBlobBufSize, 128, "l_svp_nnie_BlobBuf", "BlobBuf") < 0)
    {
        goto FAIL;
    }

    /* 为第0网络段源blob分配内存 */
    u64PhyAddr = pnnieparameter->stBlobBuf.u64PhyAddr;
    pu8VirAddr = (HI_U8*)(HI_UL)pnnieparameter->stBlobBuf.u64VirAddr;
    for(int i = 0; i < pnnieparameter->stModel.astSeg[0].u16SrcNum; i++)
    {
        pnnieparameter->astSegData[0].astSrc[i].u64PhyAddr = u64PhyAddr;
        pnnieparameter->astSegData[0].astSrc[i].u64VirAddr = (HI_U64)(HI_UL)pu8VirAddr;
        u64PhyAddr += au32SrcSize[0][i];
        pu8VirAddr += au32SrcSize[0][i];
    }
    /* 为各网络段输出blobs分配内存 */
    for(int k = 0; k < pnnieparameter->stModel.u32NetSegNum; k++)
    {
        for(int i = 0; i < pnnieparameter->stModel.astSeg[k].u16DstNum; i++)
        {
            pnnieparameter->astSegData[k].astDst[i].u64PhyAddr = u64PhyAddr;
            pnnieparameter->astSegData[k].astDst[i].u64VirAddr = (HI_U64)(HI_UL)pu8VirAddr;
            u64PhyAddr += au32DstSize[k][i];
            pu8VirAddr += au32DstSize[k][i];
        }
    }

    //------------------------------------------------------------
    // 填充前向计算控制结构体。
    // NOTE:此处填充的信息为网络模型辅助内存分配位置和大小信息，以
    //      及各网络段所需任务内存分配位置和大小信息。
    // ATTENTION:当前仅为各段网络分配同一NNIE引擎，因并未考虑适配
    //           Hi3559A。
    //------------------------------------------------------------
    HI_U32 u32Offset = 0;
    for(int i = 0; i < pnnieparameter->stModel.u32NetSegNum; i++)
    {
        /* 类型检查，当前对CNN外的其它类型未作处理 */
        if(SVP_NNIE_NET_TYPE_CNN != pnnieparameter->stModel.astSeg[i].enNetType)
        {
            LOGE("[SVP-NNIE-ERROR]Unprocessed NetType! Please do code review.\n");
            goto FAIL;
        }
        pnnieparameter->astForwardCtrl[i].enNnieId            = SVP_NNIE_ID_0;
        pnnieparameter->astForwardCtrl[i].u32SrcNum           = pnnieparameter->stModel.astSeg[i].u16SrcNum;
        pnnieparameter->astForwardCtrl[i].u32DstNum           = pnnieparameter->stModel.astSeg[i].u16DstNum;
        pnnieparameter->astForwardCtrl[i].u32NetSegId         = i;
        pnnieparameter->astForwardCtrl[i].stTmpBuf            = pnnieparameter->stTmpBuf;
        pnnieparameter->astForwardCtrl[i].stTskBuf.u64PhyAddr = pnnieparameter->stTaskBuf.u64PhyAddr + u32Offset;
        pnnieparameter->astForwardCtrl[i].stTskBuf.u64VirAddr = pnnieparameter->stTaskBuf.u64VirAddr + u32Offset;
        pnnieparameter->astForwardCtrl[i].stTskBuf.u32Size    = au32TaskBufSize[i];
        u32Offset += au32TaskBufSize[i];
    }

    LOGI("[SVP-NNIE-INFO]NNIE model is ready.\n");

    return 0;

FAIL:
    L_SVP_NNIE_DePrepareModel(&nnieparameter);
    return -1;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：L_SVP_NNIE_UnloadModel
//|  功能描述：SVP-NNIE模型卸载
//|  参数说明：L_NNIE_PARA_S结构体指针
//|  返回值说明：无
//|  备注：模型的卸载实质是释放*.wk文件占用的MMZ内存空间，并重置该模型的描述结构体。
//+------------------------------------------------------------------------------------------+
static void L_SVP_NNIE_UnloadModel(L_NNIE_PARA_S *pnnieparameter)
{
    /* 卸载模型 */
    HI_MPI_SVP_NNIE_UnloadModel(&pnnieparameter->stModel);

    /* 释放模型内存空间 */
    L_SVP_NNIE_ReleaseMMZ(&pnnieparameter->stModelBuf, "Model");
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：L_SVP_NNIE_LoadModel
//|  功能描述：SVP-NNIE模型加载
//|  参数说明：模型文件位置，L_NNIE_PARA_S结构体指针
//|  返回值说明：成功返回0
//|  备注：模型加载的实质是将NNIE编译器得到的*.wk文件存放在为其开辟的MMZ空间中，并使用API获得
//|        该模型的描述结构体。
//+------------------------------------------------------------------------------------------+
static int L_SVP_NNIE_LoadModel(const char *ppath_to_wk_file, L_NNIE_PARA_S *pnnieparameter)
{
    int  ret = 0;
    long filesize = 0;
    HI_U8 *pu8VirAddr = NULL;

    if(ppath_to_wk_file == NULL){LOGE("[SVP-NNIE-ERROR]Model file is NULL!\n");return -1;}
    LOGI("[SVP-NNIE-INFO]Open model file:%s...\n", ppath_to_wk_file);
    //----------------------------------------
    //用户需事先开辟好模型存储空间，且将
    //NNIE编译器得到的wk文件加载到该缓冲区中。
    //——《参考》P37
    //----------------------------------------
    FILE *fp = fopen(ppath_to_wk_file, "rb");
    if(fp == NULL){LOGE("[SVP-NNIE-ERROR]fopen:Cannot open model file!\n");goto FAIL_0;}

    ret = fseek(fp, 0L, SEEK_END);
    if(ret < 0){LOGE("[SVP-NNIE-ERROR]fseek:Cannot move model file pointer to end!\n");goto FAIL_0;}

    filesize = ftell(fp);
    if(filesize < 0){LOGE("[SVP-NNIE-ERROR]ftell:Unable to calculate model file size!\n");goto FAIL_0;}

    ret = fseek(fp, 0L, SEEK_SET);
    if(ret < 0){LOGE("[SVP-NNIE-ERROR]fseek:Cannot move model file pointer to start!\n");goto FAIL_0;}

    //----------------------------------------
    //根据计算得到的文件大小，开辟缓冲区空间
    //开辟的缓冲区来自MMZ内存
    //----------------------------------------
    if(L_SVP_NNIE_RequestMMZ(&pnnieparameter->stModelBuf, filesize, 0, "l_svp_nnie_model_file", "Model") < 0)
    {
        goto FAIL_1;
    }

    //----------------------------------------
    //关于fread函数，fread(buf,item,count,fp)
    //其每次读item大小的数据块，分count次来读，
    //返回值为成功有效的读取的item元素的个数
    //----------------------------------------
    pu8VirAddr = (HI_U8*)(HI_UL)pnnieparameter->stModelBuf.u64VirAddr;
    ret = fread(pu8VirAddr, filesize, 1, fp);
    if(ret != 1){LOGE("[SVP-NNIE-ERROR]fread:Cannot read model file!\n");goto FAIL_1;}

    //----------------------------------------
    //载入模型，取得模型结构体
    //----------------------------------------
    ret = HI_MPI_SVP_NNIE_LoadModel(&pnnieparameter->stModelBuf, &pnnieparameter->stModel);
    if(ret != HI_SUCCESS){LOGE("[SVP-NNIE-ERROR]HI_MPI_SVP_NNIE_LoadModel:failed with %#x!\n", ret);goto FAIL_1;}

    LOGI("[SVP-NNIE-INFO]Load model done.\n");

    fclose(fp);
    return 0;

FAIL_1:
    L_SVP_NNIE_UnloadModel(pnnieparameter);

FAIL_0:
    if(fp != NULL)
    {
        fclose(fp);
    }
    return -1;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：L_SVP_NNIE_Init
//|  功能描述：模型载入和网络软硬件参数初始化。
//|  参数说明：模型文件位置
//|  返回值说明：成功返回0
//|  备注：
//+------------------------------------------------------------------------------------------+
int L_SVP_NNIE_Init(const char *ppath_to_wk_file)
{
    int ret = 0;

    do
    {
        /* 模型装载 */
        ret = L_SVP_NNIE_LoadModel(ppath_to_wk_file, &nnieparameter);
        if(ret != 0){LOGE("[SVP-NNIE-ERROR]L_SVP_NNIE_LoadModel:failed with %d!\n", ret);break;}

        /* 模型准备 */
        ret = L_SVP_NNIE_PrepareModel(&nnieparameter);
        if(ret != 0)
        {
            LOGE("[SVP-NNIE-ERROR]L_SVP_NNIE_PrepareModel:failed with %d!\n", ret);
            L_SVP_NNIE_DePrepareModel(&nnieparameter);
            break;
        }

        /* 网络输出准备 */
        ret = L_SVP_NNIE_PrepareOutput(&nnieparameter, &yoloparameter);
        if(ret != 0)
        {
            LOGE("[SVP-NNIE-ERROR]L_SVP_NNIE_PrepareOutput:failed with %d!\n", ret);
            L_SVP_NNIE_DePrepareOutput(&yoloparameter);
            break;
        }

        return 0;
    }while(0);

    return ret;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：L_SVP_NNIE_DeInit
//|  功能描述：SVP-NNIE去初始化
//|  参数说明：无
//|  返回值说明：无
//|  备注：
//+------------------------------------------------------------------------------------------+
void L_SVP_NNIE_DeInit(void)
{
    L_SVP_NNIE_DePrepareOutput(&yoloparameter);
    L_SVP_NNIE_DePrepareModel(&nnieparameter);
    L_SVP_NNIE_UnloadModel(&nnieparameter);
}
